/**
 * 
 */
package marpg.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import marpg.network.ConnectionHandler;
import marpg.protocol.ActionName;
import marpg.protocol.ActionType;
import marpg.protocol.ContentName;
import marpg.protocol.MARPG;
import marpg.protocol.MessageBody;
import marpg.protocol.MessageBody.Content;
import marpg.protocol.MessageHandler;

/**
 * @author Steven
 * 
 */
public abstract class Agent {
	public static final String SERVER_NAME = "__SERVER__";
	private String agentName = null;

	private ConnectionHandler connectionHandler = null;
	private MessageHandler messageHandler = new MessageHandler();

	private Map<Long, MARPG> sentMessageQueue = new HashMap<Long, MARPG>();

	private int currentHp;
	private int currentHv;
	private int currentTv;
	private Location goalLocation;
	private Location currentLocation;
	private Map<Item, Integer> currentBackpack;
	private List<String> agentList;

	public abstract void setup();

	/**
	 * @return Agent name.
	 */
	public String getAgentName() {
		return agentName;
	}

	/**
	 * @param agentName
	 */
	public void setAgentName(String agentName) {
		this.agentName = agentName;
	}

	/**
	 * @return Connection handler.
	 */
	public ConnectionHandler getConnectionHandler() {
		return connectionHandler;
	}

	/**
	 * @param connectionHandler
	 */
	public void setConnectionHandler(ConnectionHandler connectionHandler) {
		this.connectionHandler = connectionHandler;
	}

	/**
	 * @return the currentHp
	 */
	public int getCurrentHp() {
		return currentHp;
	}

	/**
	 * @param currentHp
	 *            the currentHp to set
	 */
	public void setCurrentHp(int currentHp) {
		this.currentHp = currentHp;
	}

	/**
	 * @return the currentHv
	 */
	public int getCurrentHv() {
		return currentHv;
	}

	/**
	 * @param currentHv
	 *            the currentHv to set
	 */
	public void setCurrentHv(int currentHv) {
		this.currentHv = currentHv;
	}

	/**
	 * @return the currentTv
	 */
	public int getCurrentTv() {
		return currentTv;
	}

	/**
	 * @param currentTv
	 *            the currentTv to set
	 */
	public void setCurrentTv(int currentTv) {
		this.currentTv = currentTv;
	}

	/**
	 * @return the goalLocation
	 */
	public Location getGoalLocation() {
		return goalLocation;
	}

	/**
	 * @param goalLocation
	 */
	public void setGoalLocation(Location goalLocation) {
		this.goalLocation = goalLocation;
	}

	/**
	 * @return the currentLocation
	 */
	public Location getCurrentLocation() {
		return currentLocation;
	}

	/**
	 * @param currentLocation
	 *            the currentLocation to set
	 */
	public void setCurrentLocation(Location currentLocation) {
		this.currentLocation = currentLocation;
	}

	/**
	 * @return the currentBackpack
	 */
	public Map<Item, Integer> getCurrentBackpack() {
		return currentBackpack;
	}

	/**
	 * @param currentBackpack
	 *            the currentBackpack to set
	 */
	public void setCurrentBackpack(Map<Item, Integer> currentBackpack) {
		this.currentBackpack = currentBackpack;
	}

	/**
	 * @return the agentList
	 */
	public List<String> getAgentList() {
		return agentList;
	}

	/**
	 * @param agentList
	 *            the agentList to set
	 */
	public void setAgentList(List<String> agentList) {
		this.agentList = agentList;
	}

	/**
	 * @param message
	 */
	public void receiveMessage(String message) {
		// Unmarshal message
		MARPG marpgObj = messageHandler.decomposeMessage(message);
		receiveMessage(marpgObj);
	}

	/**
	 * React to received messages.
	 * 
	 * @param message
	 */
	public void receiveMessage(MARPG message) {
		if (message == null) {
			System.out.println("Null message.");
		} else {
			long messageId = message.getId();
			long responseId = message.getResponseId();
			String fromAgent = message.getSender();
			MessageBody messageBody = message.getMessageBody();
			ActionType actionType = messageBody.getType();
			ActionName actionName = messageBody.getName();
			List<Content> contentList = messageBody.getContent();

			try {
				switch (actionType) {
				case QUERY:
					switch (actionName) {
					case BACKPACK:
						break;

					case LOCATION:
						break;

					case HP:
						break;

					case HV:
						break;

					case TV:
						break;
					}
					break;

				case REQUEST:
					switch (actionName) {
					case MOVE:
						break;

					case GET:
						break;

					case GIVE:
						break;
					}
					break;

				case RESPONSE:
					// Other agent responds to our previous sent message.
					// Get what we queried/requested.
					if (!sentMessageQueue.containsKey(responseId)) {
						break;
					}

					ActionName previousSentMessage = sentMessageQueue.get(responseId).getMessageBody().getName();
					boolean isAccepted = (actionName == ActionName.ACCEPT);

					switch (previousSentMessage) {
					case LOGIN:
						recvDoLoginResponse(isAccepted);

					case BACKPACK:
						recvQueryBackpackResponse(responseId, fromAgent, isAccepted);
						break;

					case LOCATION:
						break;

					case HP:
						break;

					case HV:
						break;

					case TV:
						break;

					case MOVE:
						List<Content> contents = sentMessageQueue.get(responseId).getMessageBody().getContent();
						if (!contents.isEmpty()) {
							Location location;
							if (contents.get(0).getName() == ContentName.X) {
								location = new Location(Integer.parseInt(contents.get(0).getValue()),
										(Integer.parseInt(contents.get(1).getValue())));
							} else {
								location = new Location(Integer.parseInt(contents.get(1).getValue()),
										(Integer.parseInt(contents.get(0).getValue())));
							}
							recvDoMoveResponse(isAccepted, location);
						}
						break;

					case GET:
						break;

					case GIVE:
						break;
					}

					// Remove the message from sendMessageQueue.
					sentMessageQueue.remove(responseId);
					break;

				case INFORMATION:
					switch (actionName) {
					case GOAL:
						if (!contentList.isEmpty()) {
							Location location;
							if (contentList.get(0).getName() == ContentName.X) {
								location = new Location(Integer.parseInt(contentList.get(0).getValue()),
										(Integer.parseInt(contentList.get(1).getValue())));
							} else {
								location = new Location(Integer.parseInt(contentList.get(1).getValue()),
										(Integer.parseInt(contentList.get(0).getValue())));
							}

							recvGoalInformation(location);
						}
						break;

					case AGENT_LIST:
						if (!contentList.isEmpty()) {
							List<String> agents = new ArrayList<String>();

							for (Content content : contentList) {
								if (content.getName().equals(ContentName.AGENT.name())) {
									agents.add(content.getValue());
								}
							}

							recvAgentListInformation(agents);
						}
						break;

					case BACKPACK:
						break;

					case LOCATION:
						if (!contentList.isEmpty()) {
							Location location;
							if (contentList.get(0).getName() == ContentName.X) {
								location = new Location(Integer.parseInt(contentList.get(0).getValue()),
										(Integer.parseInt(contentList.get(1).getValue())));
							} else {
								location = new Location(Integer.parseInt(contentList.get(1).getValue()),
										(Integer.parseInt(contentList.get(0).getValue())));
							}

							recvAgentLocationInformation(fromAgent, location);
						}
						break;

					case HP:
						break;

					case HV:
						break;

					case TV:
						break;
					}
					break;

				case DO:
					switch (actionName) {
					case GIVE:
						break;

					case GET:
						break;
					}
					break;
				}
			} catch (NullPointerException e) {
				System.err.println("Missing required XML elements!");
				System.err.println(e.getMessage());
			}
		}
	}

	public abstract void recvDoLoginResponse(boolean isAccepted);

	public abstract void recvDoMoveResponse(boolean isAccepted, Location location);

	public abstract void recvGoalInformation(Location location);

	public abstract void recvAgentListInformation(List<String> agentList);

	public abstract void recvItemLocationInformation(List<Location> locations);

	public abstract void recvQueryBackpack(long messageId, String fromAgent);

	public abstract void recvQueryBackpackResponse(long responseId, String fromAgent, boolean isAccepted);

	public abstract void recvBackpackInformation(String fromAgent, Map<Item, Integer> itemQuantity);

	public abstract void recvQueryStatus(long messageId, String fromAgent);

	public abstract void recvQueryStatusResponse(long responseId, String fromAgent, boolean isAccepted);

	public abstract void recvStatusInformation(String fromAgent, Map<Status, Integer> statusValue);

	public abstract void recvQueryAgentLocation(long messageId, String fromAgent);

	public abstract void recvQueryAgentLocationResponse(long responseId, String fromAgent, boolean isAccepted);

	public abstract void recvAgentLocationInformation(String fromAgent, Location location);

	public abstract void recvRequestMove(long messageId, String fromAgent, Location location);

	public abstract void recvRequestMoveResponse(long responseId, String fromAgent, boolean isAccepted);

	public abstract void recvRequestGiveItem(long messageId, String fromAgent, Map<Item, Integer> itemQuantity);

	public abstract void recvRequestGiveItemResponse(long responseId, String fromAgent, boolean isAccepted,
			Map<Item, Integer> proposedItemQuantity);

	public abstract void recvDoGiveItem(long messageId, Map<Item, Integer> itemQuantity);

	public abstract void recvRequestGetItem(long messageId, String fromAgent, Map<Item, Integer> itemQuantity);

	public abstract void recvRequestGetItemResponse(long responseId, String fromAgent, boolean isAccepted,
			Map<Item, Integer> proposedItemQuantity);

	public abstract void recvDoGetItem(long messageId, Map<Item, Integer> itemQuantity);

	/**
	 * @param message
	 */
	public void sendMessage(MARPG message, boolean isAddSentMessageQueue) {
		// Set message ID as the current timestamp.
		message.setId(System.currentTimeMillis());
		message.setSender(agentName);

		// Send message via connectionHandler
		connectionHandler.sendMessage(messageHandler.composeMessage(message));

		// Put message into sendMessageQueue
		if (isAddSentMessageQueue) {
			sentMessageQueue.put(message.getId(), message);
		}
	}

	/**
	 * Send login request to server.
	 */
	public void doLogin() {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.DO);
		messageBody.setName(ActionName.LOGIN);
		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, true);
	}

	/**
	 * Send logout request to server.
	 */
	public void doLogout() {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.DO);
		messageBody.setName(ActionName.LOGOUT);
		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Agent moves to a location.
	 * 
	 * @param location
	 */
	public void doMove(Location location) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);

		// ResponseId is not required here.
		// message.setResponseId(0);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.DO);
		messageBody.setName(ActionName.MOVE);

		// Fill in content
		MessageBody.Content content;

		content = new MessageBody.Content();
		content.setName(ContentName.X);
		content.setValue(location.x().toString());
		messageBody.getContent().add(content);

		content = new MessageBody.Content();
		content.setName(ContentName.Y);
		content.setValue(location.y().toString());
		messageBody.getContent().add(content);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, true);
	}

	/**
	 * Agent uses an item.
	 * 
	 * @param item
	 * @param quantity
	 */
	public void doUseItem(Item item, int quantity) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.DO);
		messageBody.setName(ActionName.USE);

		// Fill in content
		MessageBody.Content content;
		content = new MessageBody.Content();
		content.setName(ContentName.fromValue(item.name()));
		messageBody.getContent().add(content);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Agent gathers an item at a location.
	 * 
	 * @param item
	 * @param quantity
	 * @param x
	 * @param y
	 */
	public void doGatherItem(Item item, int quantity, Location location) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.DO);
		messageBody.setName(ActionName.GATHER);

		// Fill in content
		MessageBody.Content content;

		content = new MessageBody.Content();
		content.setName(ContentName.fromValue(item.name()));
		messageBody.getContent().add(content);

		content = new MessageBody.Content();
		content.setName(ContentName.X);
		content.setValue(location.x().toString());
		messageBody.getContent().add(content);

		content = new MessageBody.Content();
		content.setName(ContentName.Y);
		content.setValue(location.y().toString());
		messageBody.getContent().add(content);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Query server to return goal location.
	 */
	public void queryGoal() {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.QUERY);
		messageBody.setName(ActionName.GOAL);
		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Query server to return a list of all agents.
	 */
	public void queryAgentList() {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.QUERY);
		messageBody.setName(ActionName.AGENT_LIST);
		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Query server to get the location of the specified item.
	 * 
	 * @param item
	 */
	public void queryItemLocation(Item item) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.QUERY);
		messageBody.setName(ActionName.LOCATION);

		// Fill in content
		MessageBody.Content content;

		content = new MessageBody.Content();
		content.setName(ContentName.fromValue(item.name()));
		messageBody.getContent().add(content);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Query other agent's fruit and water reserve.
	 * 
	 * @param contactAgent
	 */
	public void queryBackpack(String contactAgent) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(contactAgent);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.QUERY);
		messageBody.setName(ActionName.BACKPACK);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, true);
	}

	/**
	 * Respond to other agent's backpack query.
	 * 
	 * @param correspondingMsgId
	 * @param accepted
	 */
	public void respondBackpackQuery(long correspondingMsgId, boolean isAccepted) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);
		message.setResponseId(correspondingMsgId);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.RESPONSE);
		messageBody.setName(isAccepted ? ActionName.ACCEPT : ActionName.REJECT);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Provide fruit and water information to contactAgent.
	 * 
	 * @param contactAgent
	 * @param itemQuantity
	 */
	public void informBackpack(String contactAgent, Map<Item, Integer> itemQuantity) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);

		// ResponseId is not required here.
		// message.setResponseId(0);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.INFORMATION);
		messageBody.setName(ActionName.BACKPACK);

		// Fill in content
		MessageBody.Content content;

		for (Map.Entry<Item, Integer> entry : itemQuantity.entrySet()) {
			content = new MessageBody.Content();
			content.setName(ContentName.fromValue(entry.getKey().name()));
			content.setValue(entry.getValue().toString());
			messageBody.getContent().add(content);
		}

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Query other agent's HP, HV, and TV.
	 * 
	 * @param contactAgent
	 */
	public void queryStatus(String contactAgent, Status status) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(contactAgent);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.QUERY);
		messageBody.setName(ActionName.fromValue(status.name()));

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, true);
	}

	/**
	 * Respond to other agent's status query.
	 * 
	 * @param correspondingMsgId
	 * @param accepted
	 */
	public void respondStatusQuery(long correspondingMsgId, boolean isAccepted) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);
		message.setResponseId(correspondingMsgId);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.RESPONSE);
		messageBody.setName(isAccepted ? ActionName.ACCEPT : ActionName.REJECT);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Provide HP, HV, TV information to contactAgent.
	 * 
	 * @param contactAgent
	 * @param statusValue
	 */
	public void informStatus(String contactAgent, Status status, int statusValue) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);

		// ResponseId is not required here.
		// message.setResponseId(0);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.INFORMATION);
		messageBody.setName(ActionName.BACKPACK);

		// Fill in content
		MessageBody.Content content;
		content = new MessageBody.Content();
		content.setName(ContentName.fromValue(status.name()));
		content.setValue(Integer.toString(statusValue));
		messageBody.getContent().add(content);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Query other agent's location.
	 * 
	 * @param contactAgent
	 */
	public void queryAgentLocation(String contactAgent) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(contactAgent);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.QUERY);
		messageBody.setName(ActionName.LOCATION);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, true);
	}

	/**
	 * Respond to other agent's location query.
	 * 
	 * @param correspondingMsgId
	 * @param accepted
	 */
	public void respondAgentLocationQuery(long correspondingMsgId, boolean isAccepted) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);
		message.setResponseId(correspondingMsgId);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.RESPONSE);
		messageBody.setName(isAccepted ? ActionName.ACCEPT : ActionName.REJECT);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Provide location information to contactAgent.
	 * 
	 * @param contactAgent
	 * @param location
	 */
	public void informAgentLocation(String contactAgent, Location location) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);

		// ResponseId is not required here.
		// message.setResponseId(0);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.INFORMATION);
		messageBody.setName(ActionName.LOCATION);

		// Fill in content
		MessageBody.Content content;

		content = new MessageBody.Content();
		content.setName(ContentName.X);
		content.setValue(location.x().toString());
		messageBody.getContent().add(content);

		content = new MessageBody.Content();
		content.setName(ContentName.Y);
		content.setValue(location.y().toString());
		messageBody.getContent().add(content);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Agent requests another agent to move to a location.
	 * 
	 * @param contactAgent
	 * @param location
	 */
	public void requestMove(String contactAgent, Location location) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(contactAgent);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.REQUEST);
		messageBody.setName(ActionName.MOVE);

		// Fill in content
		MessageBody.Content content;

		content = new MessageBody.Content();
		content.setName(ContentName.AGENT);
		content.setValue(contactAgent);
		messageBody.getContent().add(content);

		content = new MessageBody.Content();
		content.setName(ContentName.X);
		content.setValue(location.x().toString());
		messageBody.getContent().add(content);

		content = new MessageBody.Content();
		content.setName(ContentName.Y);
		content.setValue(location.y().toString());
		messageBody.getContent().add(content);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, true);
	}

	/**
	 * Agent responds to a move request from other agent.
	 * 
	 * @param correspondingMsgId
	 * @param accepted
	 */
	public void respondMoveRequest(long correspondingMsgId, boolean isAccepted) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);
		message.setResponseId(correspondingMsgId);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.RESPONSE);
		messageBody.setName(isAccepted ? ActionName.ACCEPT : ActionName.REJECT);

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Agent requests contactAgent to give item to toAgent.
	 * 
	 * @param contactAgent
	 * @param toAgent
	 * @param itemQuantity
	 */
	public void requestGiveItem(String contactAgent, String toAgent, Map<Item, Integer> itemQuantity) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(contactAgent);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.QUERY);
		messageBody.setName(ActionName.GIVE);

		// Fill in content
		MessageBody.Content content;

		content = new MessageBody.Content();
		content.setName(ContentName.AGENT);
		content.setValue(toAgent);
		messageBody.getContent().add(content);

		for (Map.Entry<Item, Integer> entry : itemQuantity.entrySet()) {
			content = new MessageBody.Content();
			content.setName(ContentName.fromValue(entry.getKey().name()));
			content.setValue(entry.getValue().toString());
			messageBody.getContent().add(content);
		}

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, true);
	}

	/**
	 * Agent responds to a give request from other agent. If accepted, set proposedItemQuantity to null. If rejected,
	 * agent can make a new proposal in proposedItemQuantity.
	 * 
	 * @param correspondingMsgId
	 * @param accepted
	 * @param proposedItemQuantity
	 */
	public void respondGiveItemRequest(long correspondingMsgId, boolean isAccepted,
			Map<Item, Integer> proposedItemQuantity) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);
		message.setResponseId(correspondingMsgId);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.RESPONSE);
		messageBody.setName(isAccepted ? ActionName.ACCEPT : ActionName.REJECT);

		if (!isAccepted) {
			// Fill in content
			MessageBody.Content content;

			for (Map.Entry<Item, Integer> entry : proposedItemQuantity.entrySet()) {
				content = new MessageBody.Content();
				content.setName(ContentName.fromValue(entry.getKey().name()));
				content.setValue(entry.getValue().toString());
				messageBody.getContent().add(content);
			}
		}

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Actually gives items to contactAgent.
	 * 
	 * @param contactAgent
	 * @param itemQuantity
	 */
	public void doGiveItem(String contactAgent, Map<Item, Integer> itemQuantity) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(contactAgent);

		// ResponseId is not required here.
		// message.setResponseId(0);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.DO);
		messageBody.setName(ActionName.GIVE);

		// Fill in content
		MessageBody.Content content;

		for (Map.Entry<Item, Integer> entry : itemQuantity.entrySet()) {
			content = new MessageBody.Content();
			content.setName(ContentName.fromValue(entry.getKey().name()));
			content.setValue(entry.getValue().toString());
			messageBody.getContent().add(content);
		}

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Agent requests contactAgent to get item from fromAgent.
	 * 
	 * @param contactAgent
	 * @param fromAgent
	 * @param itemQuantity
	 */
	public void requestGetItem(String contactAgent, String fromAgent, Map<Item, Integer> itemQuantity) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(contactAgent);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.QUERY);
		messageBody.setName(ActionName.GET);

		// Fill in content
		MessageBody.Content content;

		content = new MessageBody.Content();
		content.setName(ContentName.AGENT);
		content.setValue(fromAgent);
		messageBody.getContent().add(content);

		for (Map.Entry<Item, Integer> entry : itemQuantity.entrySet()) {
			content = new MessageBody.Content();
			content.setName(ContentName.fromValue(entry.getKey().name()));
			content.setValue(entry.getValue().toString());
			messageBody.getContent().add(content);
		}

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, true);
	}

	/**
	 * Agent responds to a give request from other agent. If accept, set proposedItemQuantity to null. If reject, agent
	 * can make a new proposal in proposedItemQuantity.
	 * 
	 * @param correspondingMsgId
	 * @param accepted
	 * @param proposedItemQuantity
	 */
	public void respondGetItemRequest(long correspondingMsgId, boolean isAccepted,
			Map<Item, Integer> proposedItemQuantity) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(SERVER_NAME);
		message.setResponseId(correspondingMsgId);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.RESPONSE);
		messageBody.setName(isAccepted ? ActionName.ACCEPT : ActionName.REJECT);

		if (!isAccepted) {
			// Fill in content
			MessageBody.Content content;

			for (Map.Entry<Item, Integer> entry : proposedItemQuantity.entrySet()) {
				content = new MessageBody.Content();
				content.setName(ContentName.fromValue(entry.getKey().name()));
				content.setValue(entry.getValue().toString());
				messageBody.getContent().add(content);
			}
		}

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Actually gets items from contactAgent.
	 * 
	 * @param contactAgent
	 * @param itemQuantity
	 */
	public void doGetItem(String contactAgent, Map<Item, Integer> itemQuantity) {
		// Construct MARPG message object.
		MARPG message = new MARPG();
		message.setReceiver(contactAgent);

		// ResponseId is not required here.
		// message.setResponseId(0);

		MessageBody messageBody = new MessageBody();
		messageBody.setType(ActionType.DO);
		messageBody.setName(ActionName.GET);

		// Fill in content
		MessageBody.Content content;

		for (Map.Entry<Item, Integer> entry : itemQuantity.entrySet()) {
			content = new MessageBody.Content();
			content.setName(ContentName.fromValue(entry.getKey().name()));
			content.setValue(entry.getValue().toString());
			messageBody.getContent().add(content);
		}

		message.setMessageBody(messageBody);

		// Send message
		sendMessage(message, false);
	}

	/**
	 * Send customized message to contactAgent.
	 * 
	 * @param contactAgent
	 * @param content
	 */
	public void sendCustomizedMessage(String contactAgent, String content) {
		// TODO Add code here.
	}

	/**
	 * Respond to other agent's customized message.
	 * 
	 * @param correspondingMsgId
	 * @param accepted
	 * @param content
	 */
	public void respondCustomizedMessage(long correspondingMsgId, boolean isAccepted, String content) {
		// TODO Add code here.
	}
}
